package com.easyJava.builder;

import com.easyJava.bean.Constants;
import com.easyJava.bean.FieldInfo;
import com.easyJava.bean.TableInfo;
import com.easyJava.utils.BufferedWriterUtils;
import com.easyJava.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

@Slf4j
public class BuildMapperXml {

    private final static String BASE_RESULT_MAP = "base_result_map";
    private final static String BASE_COLUMN_LIST = "base_column_list";
    private final static String BASE_QUERY_CONDITION = "base_query_condition";
    private final static String EXTEND_QUERY_CONDITION = "extend_query_condition";
    private final static String QUERY_CONDITION = "query_condition";

    public static void execute(TableInfo tableInfo){
        File folder = new File(Constants.PATH_MAPPER_XML);
        folder.mkdirs();
        String fileName = tableInfo.getBeanName() + Constants.SUFFIX_MAPPER;
        File xmlFile = new File(folder, fileName + Constants.FILE_SUFFIX_XML);

        try (
                OutputStream out = new FileOutputStream(xmlFile);
                OutputStreamWriter outw = new OutputStreamWriter(out, StandardCharsets.UTF_8);
                BufferedWriter bufferedWriter = new BufferedWriter(outw);

        ){
            BufferedWriterUtils bw = new BufferedWriterUtils(bufferedWriter);

            //找到自增属性
            FieldInfo autoIncrement = null;
            for(FieldInfo fieldInfo : tableInfo.getFieldList()){
                if(fieldInfo.isAutoIncrement()){
                    autoIncrement = fieldInfo;
                    break;
                }
            }
            //找到索引列
            Set<String> indexNameSet = new HashSet<>();
            for(Map.Entry<String, List<FieldInfo>> entry : tableInfo.getIndexMap().entrySet()){
                List<FieldInfo> fieldInfoList = entry.getValue();
                for(FieldInfo fieldInfo : fieldInfoList){
                    indexNameSet.add(fieldInfo.getFieldName());
                }
            }


            //xml头
            bw.write("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>");
            bw.write("<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\" >");
            bw.write("<mapper namespace=\"" + Constants.PACKAGE_MAPPER + "." + fileName + "\">");

            //resultMap
            generateResultMap(tableInfo, bw);

            //通用查询结果列
            generateBaseQueryResultSQL(tableInfo, bw);

            //基础查询语句
            generateBaseQuerySQL(tableInfo, bw);

            //扩展查询语句
            generateExtendQuerySQL(tableInfo, bw);

            //查询条件
            generateQueryCondition(bw);

            //查询列表
            generateSelectList(tableInfo, bw);

            //查询数量
            generateSelectCount(tableInfo, bw);

            //插入单条数据
            generateInsert(tableInfo, bw, autoIncrement);

            //插入或更新一条数据
            generateInsertOrUpdate(tableInfo, bw, indexNameSet);

            //批量插入
            generateInsertBatch(tableInfo, bw, autoIncrement);

            //批量插入或修改
            generateInsertOrUpdateBatch(tableInfo, bw, indexNameSet);

            //根据唯一索引生成删改查
            generateRUDByUniqueIndex(tableInfo, bw, indexNameSet);

            //xml尾
            bw.newLine();
            bw.write("</mapper>");

            bw.flush();
        }catch (Exception e){
            log.error("创建xml失败:{}", e);
        }
    }

    private static void generateInsertBatch(TableInfo tableInfo, BufferedWriterUtils bw, FieldInfo autoIncrement) throws IOException {
        String generatedKey = "";
        if(autoIncrement != null){
            generatedKey = " useGeneratedKeys=\"true\" keyProperty=\"" + autoIncrement.getBeanFieldName() + "\"";
        }
        bw.newLine();
        BuildComment.createXmlComment(bw, "批量插入", 1);
        bw.write("\t<insert id=\"insertBatch\" parameterType=\"" + Constants.PACKAGE_PO + "." + tableInfo.getBeanName() + "\"" + generatedKey + ">");
        StringBuffer insertField = new StringBuffer();
        StringBuffer insertFieldExpression = new StringBuffer();
        for(FieldInfo fieldInfo : tableInfo.getFieldList()){
            if(fieldInfo.isAutoIncrement()) continue;
            insertField.append(fieldInfo.getFieldName()).append(",");
            insertFieldExpression.append("#{item." + fieldInfo.getBeanFieldName() + "},");
        }
        String insertFieldStr = insertField.substring(0, insertField.lastIndexOf(","));
        String insertFieldExpressionStr = insertFieldExpression.substring(0, insertFieldExpression.lastIndexOf(","));
        bw.write("\t\tinsert into " + tableInfo.getTableName() + "(" + insertFieldStr + ") values");
        bw.write("\t\t<foreach collection=\"list\" item=\"item\" separator=\",\">");
        bw.write("\t\t\t( " + insertFieldExpressionStr + ")");
        bw.write("\t\t</foreach>");
        bw.write("\t</insert>");
    }

    private static void generateInsertOrUpdate(TableInfo tableInfo, BufferedWriterUtils bw, Set<String> indexNameSet) throws IOException {
        bw.newLine();
        BuildComment.createXmlComment(bw, "插入或更新一条数据" ,1);
        bw.write("\t<insert id=\"insertOrUpdate\" parameterType=\"" + Constants.PACKAGE_PO + "." + tableInfo.getBeanName() + "\">");
        bw.write("\t\tinsert into " + tableInfo.getTableName());
        bw.write("\t\t<trim prefix=\"(\" suffix=\")\" suffixOverrides=\",\">");
        for(FieldInfo fieldInfo : tableInfo.getFieldList()){
            String extend = "";
            if(ArrayUtils.contains(Constants.SQL_STRING_TYPE, fieldInfo.getSqlType())){
                extend = " and bean." + fieldInfo.getBeanFieldName() + " != ''";
            }
            bw.write("\t\t\t<if test=\"bean." + fieldInfo.getBeanFieldName() + " != null" + extend +"\">");
            bw.write("\t\t\t\t" + fieldInfo.getFieldName() + ",");
            bw.write("\t\t\t</if>");
        }
        bw.write("\t\t</trim>");
        bw.write("\t\t<trim prefix=\"values (\" suffix=\")\" suffixOverrides=\",\">");
        for(FieldInfo fieldInfo : tableInfo.getFieldList()){
            String extend = "";
            if(ArrayUtils.contains(Constants.SQL_STRING_TYPE, fieldInfo.getSqlType())){
                extend = " and bean." + fieldInfo.getBeanFieldName() + " != ''";
            }
            bw.write("\t\t\t<if test=\"bean." + fieldInfo.getBeanFieldName() + " != null" + extend +"\">");
            bw.write("\t\t\t\t#{bean." + fieldInfo.getBeanFieldName() + "},");
            bw.write("\t\t\t</if>");
        }
        bw.write("\t\t</trim>");
        bw.write("\t\tON DUPLICATE KEY update");
        bw.write("\t\t<trim prefix=\"\" suffix=\"\" suffixOverrides=\",\">");
        for(FieldInfo fieldInfo : tableInfo.getFieldList()) {
            if(indexNameSet.contains(fieldInfo.getFieldName())) continue;
            String extend = "";
            if(ArrayUtils.contains(Constants.SQL_STRING_TYPE, fieldInfo.getSqlType())){
                extend = " and bean." + fieldInfo.getBeanFieldName() + " != ''";
            }
            bw.write("\t\t\t<if test=\"bean." + fieldInfo.getBeanFieldName() + " != null" + extend +"\">");
            bw.write("\t\t\t\t" + fieldInfo.getFieldName() + " = #{bean." + fieldInfo.getBeanFieldName() + "},");
            bw.write("\t\t\t</if>");
        }
        bw.write("\t\t</trim>");
        bw.write("\t</insert>");
    }

    private static void generateInsert(TableInfo tableInfo, BufferedWriterUtils bw, FieldInfo autoIncrement) throws IOException {
        bw.newLine();
        BuildComment.createXmlComment(bw, "插入单条数据" ,1);
        bw.write("\t<insert id=\"insert\" parameterType=\"" + Constants.PACKAGE_PO + "." + tableInfo.getBeanName() + "\">");
        if(autoIncrement != null){
            bw.write("\t\t<selectKey keyProperty=\"bean." + autoIncrement.getBeanFieldName() + "\" resultType=\"" + autoIncrement.getJavaType() + "\" order=\"AFTER\">");
            bw.write("\t\t\tselect last_insert_id()");
            bw.write("\t\t</selectKey>");
        }
        bw.write("\t\tinsert into " + tableInfo.getTableName());
        bw.write("\t\t<trim prefix=\"(\" suffix=\")\" suffixOverrides=\",\">");
        for(FieldInfo fieldInfo : tableInfo.getFieldList()){
            String extend = "";
            if(ArrayUtils.contains(Constants.SQL_STRING_TYPE, fieldInfo.getSqlType())){
                extend = " and bean." + fieldInfo.getBeanFieldName() + " != ''";
            }
            bw.write("\t\t\t<if test=\"bean." + fieldInfo.getBeanFieldName() + " != null" + extend +"\">");
            bw.write("\t\t\t\t" + fieldInfo.getFieldName() + ",");
            bw.write("\t\t\t</if>");
        }
        bw.write("\t\t</trim>");
        bw.write("\t\t<trim prefix=\"values (\" suffix=\")\" suffixOverrides=\",\">");
        for(FieldInfo fieldInfo : tableInfo.getFieldList()){
            String extend = "";
            if(ArrayUtils.contains(Constants.SQL_STRING_TYPE, fieldInfo.getSqlType())){
                extend = " and bean." + fieldInfo.getBeanFieldName() + " != ''";
            }
            bw.write("\t\t\t<if test=\"bean." + fieldInfo.getBeanFieldName() + " != null" + extend +"\">");
            bw.write("\t\t\t\t#{bean." + fieldInfo.getBeanFieldName() + "},");
            bw.write("\t\t\t</if>");
        }
        bw.write("\t\t</trim>");
        bw.write("\t</insert>");
    }

    private static void generateSelectCount(TableInfo tableInfo, BufferedWriterUtils bw) throws IOException {
        bw.newLine();
        BuildComment.createXmlComment(bw, "查询数量", 1);
        bw.write("\t<select id=\"selectCount\" resultType=\"java.lang.Long\">");
        bw.write("\t\tselect count(*) from " + tableInfo.getTableName() + "<include refid=\"" + QUERY_CONDITION + "\"/>");
        bw.write("\t</select>");
    }

    private static void generateSelectList(TableInfo tableInfo, BufferedWriterUtils bw) throws IOException {
        bw.newLine();
        BuildComment.createXmlComment(bw, "查询列表", 1);
        bw.write("\t<select id=\"selectList\" resultMap=\"" + BASE_RESULT_MAP + "\">");
        bw.write("\t\tselect <include refid=\"" + BASE_COLUMN_LIST + "\"/> from " + tableInfo.getTableName() + "<include refid=\"" + QUERY_CONDITION + "\"/>");
        bw.write("\t\t<if test=\"query.orderBy != null\"> order by ${query.orderBy} </if>");
        bw.write("\t\t<if test=\"query.simplePage != null\"> limit #{query.simplePage.start}, #{query.simplePage.end}</if>");
        bw.write("\t</select>");
    }

    private static void generateQueryCondition(BufferedWriterUtils bw) throws IOException {
        bw.newLine();
        BuildComment.createXmlComment(bw, "查询条件", 1);
        bw.write("\t<sql id=\"" + QUERY_CONDITION + "\">");
        bw.write("\t\t<where>");
        bw.write("\t\t\t<include refid=\"" + BASE_QUERY_CONDITION + "\"/>");
        bw.write("\t\t\t<include refid=\"" + EXTEND_QUERY_CONDITION + "\"/>");
        bw.write("\t\t</where>");
        bw.write("\t</sql>");
    }

    private static void generateExtendQuerySQL(TableInfo tableInfo, BufferedWriterUtils bw) throws IOException {
        bw.newLine();
        BuildComment.createXmlComment(bw, "扩展查询语句", 1);
        bw.write("\t<sql id=\"" + EXTEND_QUERY_CONDITION + "\">");
        for(FieldInfo fieldInfo : tableInfo.getExtendFieldList()){
            String extend = "";
            String andWhere = null;
            if(ArrayUtils.contains(Constants.SQL_STRING_TYPE, fieldInfo.getSqlType())){
                extend = " and query." + fieldInfo.getBeanFieldName() + " != ''";
                andWhere = "and " + fieldInfo.getFieldName() + " like concat('%', #{query." + fieldInfo.getBeanFieldName() + "}, '%')";
            }else if(fieldInfo.getBeanFieldName().endsWith(Constants.SUFFIX_BEAN_QUERY_TIME_START)){
                andWhere = "<![CDATA[and " + fieldInfo.getFieldName() + " >= #{query." + fieldInfo.getBeanFieldName() + "}]]>";
            }else if(fieldInfo.getBeanFieldName().endsWith(Constants.SUFFIX_BEAN_QUERY_TIME_END)){
                andWhere = "<![CDATA[and " + fieldInfo.getFieldName() + " <= #{query." + fieldInfo.getBeanFieldName() + "}]]>";
            }
            bw.write("\t\t<if test=\"query." + fieldInfo.getBeanFieldName() + " != null" + extend + "\">");
            bw.write("\t\t\t" + andWhere);
            bw.write("\t\t</if>");
        }
        bw.write("\t</sql>");
    }

    private static void generateBaseQuerySQL(TableInfo tableInfo, BufferedWriterUtils bw) throws IOException {
        bw.newLine();
        BuildComment.createXmlComment(bw, "基础查询语句", 1);
        bw.write("\t<sql id=\"" + BASE_QUERY_CONDITION + "\">");
        for(FieldInfo fieldInfo : tableInfo.getFieldList()){
            String extend = "";
            if(ArrayUtils.contains(Constants.SQL_STRING_TYPE, fieldInfo.getSqlType())){
                extend = " and query." + fieldInfo.getBeanFieldName() + " != ''";
            }
            bw.write("\t\t<if test=\"query." + fieldInfo.getBeanFieldName() + " != null" + extend + "\">");
            bw.write("\t\t\tand " + fieldInfo.getFieldName() +  " = #{query." + fieldInfo.getBeanFieldName() + "}");
            bw.write("\t\t</if>");
        }
        bw.write("\t</sql>");
    }

    private static void generateBaseQueryResultSQL(TableInfo tableInfo, BufferedWriterUtils bw) throws IOException {
        bw.newLine();
        StringBuffer columnList = new StringBuffer();
        for(FieldInfo fieldInfo : tableInfo.getFieldList()){
            columnList.append(fieldInfo.getFieldName());
            columnList.append(",");
        }
        BuildComment.createXmlComment(bw, "通用结果查询列", 1);
        bw.write("\t<sql id=\"" + BASE_COLUMN_LIST + "\">");
        bw.write("\t\t" + columnList.substring(0, columnList.lastIndexOf(",")));
        bw.write("\t</sql>");
    }

    private static void generateResultMap(TableInfo tableInfo, BufferedWriterUtils bw) throws IOException {
        bw.newLine();
        Map<String, List<FieldInfo>> indexMap = tableInfo.getIndexMap();
        String primary = null;
        for(Map.Entry<String, List<FieldInfo>> entry : indexMap.entrySet()){
            if(entry.getKey().equals("PRIMARY")){
                if(entry.getValue().size() == 1){
                    primary = entry.getValue().get(0).getBeanFieldName();
                    break;
                }else{
                    break;
                }
            }
        }
        BuildComment.createXmlComment(bw, "实体映射", 1);
        bw.write("\t<resultMap id=\"" + BASE_RESULT_MAP + "\" type=\"" + Constants.PACKAGE_PO + "." + tableInfo.getBeanName() + "\">");
        for(FieldInfo fieldInfo : tableInfo.getFieldList()){
            BuildComment.createXmlComment(bw, fieldInfo.getComment(), 2);
            String type = "result";
            if(primary != null && fieldInfo.getBeanFieldName().equals(primary)){
                type = "id";
            }
            bw.write("\t\t<" + type +" column=\"" + fieldInfo.getFieldName() + "\" property=\"" + fieldInfo.getBeanFieldName() + "\"/>");
            bw.newLine();
        }
        bw.write("\t</resultMap>");
    }

    private static void generateInsertOrUpdateBatch(TableInfo tableInfo, BufferedWriterUtils bw, Set<String> indexNameSet) throws IOException{
        bw.newLine();
        BuildComment.createXmlComment(bw, "批量新增或修改", 1);
        bw.write("\t<insert id=\"insertOrUpdateBatch\" parameterType=\"" + Constants.PACKAGE_PO + "."  + tableInfo.getBeanName() + "\">");
        StringBuffer insertField = new StringBuffer();
        StringBuffer insertBeanField = new StringBuffer();
        StringBuffer updateField = new StringBuffer();
        for(FieldInfo fieldInfo : tableInfo.getFieldList()){
            insertField.append(fieldInfo.getFieldName()).append(",");
            insertBeanField.append("#{item." + fieldInfo.getBeanFieldName() + "},");
            if(!indexNameSet.contains(fieldInfo.getFieldName())){
                updateField.append(fieldInfo.getFieldName() + " = VALUES(" + fieldInfo.getFieldName() + "),");
            }
        }
        String insertFieldStr = insertField.substring(0, insertField.lastIndexOf(","));
        String insertBeanFieldStr = insertBeanField.substring(0, insertBeanField.lastIndexOf(","));
        String updateFieldStr = updateField.substring(0, updateField.lastIndexOf(","));

        bw.write("\t\tinsert into " + tableInfo.getTableName() + "(" + insertFieldStr + ") values");
        bw.write("\t\t<foreach collection=\"list\" item=\"item\" separator=\",\">");
        bw.write("\t\t\t( " + insertBeanFieldStr + " )");
        bw.write("\t\t</foreach>");
        bw.write("\t\ton DUPLICATE key update");
        bw.write("\t\t" + updateFieldStr);
        bw.write("\t</insert>");
    }

    private static void generateRUDByUniqueIndex(TableInfo tableInfo, BufferedWriterUtils bw, Set<String> indexNameSet) throws IOException{
        bw.newLine();
        Map<String, List<FieldInfo>> indexMap = tableInfo.getIndexMap();
        for(Map.Entry<String, List<FieldInfo>> entry : indexMap.entrySet()){
            List<FieldInfo> fieldInfoList = entry.getValue();
            StringBuffer methodNameSB = new StringBuffer();
            StringBuffer querySB = new StringBuffer();
            int index = 0;
            for(FieldInfo fieldInfo : fieldInfoList){
                index++;
                methodNameSB.append(StringUtils.upperFirstChar(fieldInfo.getBeanFieldName()));
                querySB.append(fieldInfo.getFieldName() + "=#{" + fieldInfo.getBeanFieldName() + "}");
                if(index < fieldInfoList.size()){
                    methodNameSB.append("And");
                    querySB.append(" and ");
                }
            }
            String methodName = methodNameSB.toString();
            String query = querySB.toString();

            BuildComment.createXmlComment(bw, "根据" + methodName + "查询", 1);
            bw.write("\t<select id=\"selectBy" + methodName + "\" resultMap=\"" + BASE_RESULT_MAP + "\">");
            bw.write("\t\tselect <include refid=\"" + BASE_COLUMN_LIST + "\"/> from " + tableInfo.getTableName() + " where " + query);
            bw.write("\t</select>");
            bw.newLine();

            BuildComment.createXmlComment(bw, "根据" + methodName + "删除", 1);
            bw.write("\t<delete id=\"deleteBy" + methodName + "\">");
            bw.write("\t\tdelete from " + tableInfo.getTableName() + " where " + query);
            bw.write("\t</delete>");
            bw.newLine();


            BuildComment.createXmlComment(bw, "根据" + methodName + "修改", 1);
            bw.write("\t<update id=\"updateBy" + methodName + "\">");
            bw.write("\t\tupdate " + tableInfo.getTableName());
            bw.write("\t\t<set>");
            for(FieldInfo fieldInfo : tableInfo.getFieldList()){
                if(indexNameSet.contains(fieldInfo.getFieldName())) continue;
                bw.write("\t\t\t<if test=\"bean." + fieldInfo.getBeanFieldName() + " != null\">");
                bw.write("\t\t\t\t" + fieldInfo.getFieldName() + " = #{bean." + fieldInfo.getBeanFieldName() + "},");
                bw.write("\t\t\t</if>");
            }
            bw.write("\t\t</set>");
            bw.write("\t\twhere " + query);
            bw.write("\t</update>");
            bw.newLine();
        }
    }

}
